/* Copyright 2001,2002,2003 NAH6 BV
 * All Rights Reserved
 *
 *  $Header: /var/lib/cvs/secphone/ui/rng/fortuna/fnaGenerator.cpp,v 1.4 2003/11/21 16:12:58 itsme Exp $
 *
 *
 * Design of the PRNG is literally taken from 'Practical Cryptography', by Niels Ferguson, and Bruce Schneier.
 *
 */


#include "fnaGenerator.h"
#include "vectorutils.h"
#include "fnaHashFunction.h"
#include "debug.h"

//--------------------------------------------------------------------------
Generator::Generator()
{
    Initialize();
}

void Generator::Initialize()
{
    m_cypher.ClearKey();
    
    m_counter.Clear();
}

void Generator::Reseed(const ByteVector& seed)
{
    ByteVector v= m_cypher.GetKey();
    v.insert(v.end(), seed.begin(), seed.end());

    m_cypher.SetKey(HashFunction::Calculate(v));

    m_counter.Increment();
}

bool Generator::GenerateBlocks(int nBlocksRequested, ByteVector& r)
{
    if (m_counter.IsZero())
    {
        debug("Generator::GenerateBlocks: ERROR generator is unseeded\n");
        return false;                       // generator is unseeded
    }

    r.clear();

    for (int i=0 ; i<nBlocksRequested ; ++i)
    {
        ByteVector block= m_cypher.Encrypt(m_counter.GetValue());
        r.insert(r.end(), block.begin(), block.end());
        m_counter.Increment();
    }

    return true;
}

bool Generator::PseudoRandomData(int nBytesRequested, ByteVector& r)
{
    if (nBytesRequested<0 || nBytesRequested>1024*1024)
    {
        debug("Generator::PseudoRandomData: ERROR: invalid amount of data requested\n");
        return false;
    }

    if (!GenerateBlocks((nBytesRequested+BlockCypher::BLOCKSIZE-1)/BlockCypher::BLOCKSIZE, r))
    {
        r.clear();
        debug("Generator::PseudoRandomData: ERROR generating blocks for output\n");
        return false;
    }

    r.resize(nBytesRequested);

    ByteVector newkey;
    if (!GenerateBlocks(BlockCypher::KEYSIZE/BlockCypher::BLOCKSIZE, newkey))
    {
        r.clear();
        debug("Generator::PseudoRandomData: ERROR generating blocks for new key\n");
        return false;
    }
    m_cypher.SetKey(newkey);
    return true;
}

